package com.wing.file.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.wing.file.config.MinioConfig;
import com.wing.file.model.response.FileVO;
import com.wing.file.util.FileType;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;


import com.wing.file.service.MinIOFileStorageService;

import javax.servlet.http.HttpServletResponse;

/**
 * @Description: Minio实现层
 * @Project: szgc
 * @Package: com.wing.file.service.impl.MinIOFileStorageServiceImpl
 * @Date: 2025/04/28 14:23
 * @Author: heZhou
 * @Version: 1.0
 */
@Slf4j
@Service("minIOFileStorageServiceImpl")
public class MinIOFileStorageServiceImpl implements MinIOFileStorageService {

    private final String separator = "/";

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig minioConfig;

    /**
     * 上传文件
     *
     * @param multipartFile 文件
     * @param path          文件夹名称
     * @return
     * @throws Exception
     */
    @Override
    public FileVO uploadFile(MultipartFile multipartFile, String path) throws Exception {
        FileVO fileVO = new FileVO();
        String filename;
        if (multipartFile != null) {
            filename = multipartFile.getOriginalFilename();
            fileVO.setFileName(filename);
            String filePath = builderFilePath(path, filename);
            try (InputStream inputStream = multipartFile.getInputStream()) {
                // 将MultipartFile转换为InputStream
                PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                        .object(filePath).contentType(multipartFile.getContentType())
                        .bucket(minioConfig.getBucketName())
                        .stream(inputStream, multipartFile.getSize(), -1).build();
                minioClient.putObject(putObjectArgs);
                String url = minioConfig.getPreview() + separator + minioConfig.getBucketName() +
                        separator +
                        filePath;
                fileVO.setUrl(url);
                fileVO.setSrc(filePath);
                return fileVO;
            } catch (Exception e) {
                log.error("minio put file error.", e);
                throw new RuntimeException("上传文件失败");
            }
        }
        return null;
    }

    /**
     * 批量上传
     *
     * @param files
     * @param path
     * @return
     * @throws Exception
     */
    @Override
    public List<FileVO> uploadMultipartFile(MultipartFile[] files, String path) throws Exception {
        List<FileVO> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(Arrays.asList(files))) {
            InputStream inputStream = null;
            try {
                for (MultipartFile file : files) {
                    FileVO fileVO = new FileVO();
                    String filename;
                    if (file != null) {
                        filename = file.getOriginalFilename();
                        fileVO.setFileName(filename);
                        String filePath = builderFilePath(path, filename);

                        // 将MultipartFile转换为InputStream
                        inputStream = file.getInputStream();
                        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                                .object(filePath).contentType(file.getContentType())
                                .bucket(minioConfig.getBucketName())
                                .stream(inputStream, file.getSize(), -1).build();
                        minioClient.putObject(putObjectArgs);
                        String url = minioConfig.getPreview() + separator + minioConfig.getBucketName() +
                                separator +
                                filePath;
                        fileVO.setUrl(url);
                        fileVO.setSrc(filePath);
                        list.add(fileVO);
                        return list;
                    }
                }
            } catch (Exception e) {
                log.error("minio put file error.", e);
                throw new RuntimeException("上传文件失败");
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
            }
        }
        return null;
    }

    @Override
    public FileType getFileType() {
        return FileType.MINIO;
    }


    /**
     * @param dirPath
     * @param filename yyyy/mm/dd/file.jpg
     * @return
     */
    public String builderFilePath(String dirPath, String filename) {
        StringBuilder stringBuilder = new StringBuilder(50);
        if (!StringUtils.isEmpty(dirPath)) {
            stringBuilder.append(dirPath).append(separator);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String todayStr = sdf.format(new Date());
        stringBuilder.append(todayStr).append(separator);
        stringBuilder.append(filename);
        return stringBuilder.toString();
    }

    /**
     * 获取文件名
     *
     * @param fileUrl
     * @return
     */
    public String getFilePathByFileUrl(String fileUrl) {
        String filePath = fileUrl.replace(minioConfig.getPreview() + "/", "");
        int index = filePath.indexOf(separator);
        filePath = filePath.substring(index + 1);
        return filePath;
    }


    /**
     * 上传图片文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return url
     */
    @Override
    public String uploadImgFile(String prefix, String filename, InputStream inputStream) {
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder().object(filePath).contentType("image/jpg").bucket(minioConfig.getBucketName()).stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(putObjectArgs);
            return minioConfig.getPreview() + separator + minioConfig.getBucketName() +
                    separator +
                    filePath;
        } catch (Exception e) {
            log.error("minio put file error.", e);
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 上传html文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return url
     */
    @Override
    public String uploadHtmlFile(String prefix, String filename, InputStream inputStream) {
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder().object(filePath).contentType("text/html").bucket(minioConfig.getBucketName()).stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(putObjectArgs);
            return minioConfig.getEndpoint() + separator + minioConfig.getBucketName() +
                    separator +
                    filePath;
        } catch (Exception e) {
            log.error("minio put file error.", e);
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 上传文件不分类型
     *
     * @param prefix        文件前缀
     * @param filename      文件名
     * @param multipartFile 文件流
     * @return url
     */
    @Override
    public String uploadFileByMultipartFile(String prefix, String filename, MultipartFile multipartFile) {

        String filePath = builderFilePath(prefix, filename);
        try {
            // 将MultipartFile转换为InputStream
            InputStream inputStream = multipartFile.getInputStream();
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath).contentType(multipartFile.getContentType())
                    .bucket(minioConfig.getBucketName())
                    .stream(inputStream, multipartFile.getSize(), -1).build();
            minioClient.putObject(putObjectArgs);
            return minioConfig.getPreview() + separator + minioConfig.getBucketName() +
                    separator +
                    filePath;
        } catch (Exception e) {
            log.error("minio put file error.", e);
            throw new RuntimeException("上传文件失败");
        }
    }


    /**
     * 上传本地文件
     *
     * @param prefix     文件前缀
     * @param objectName 文件名称
     * @param fileName   本地文件全路径
     */
    @Override
    public String uploadFile(String prefix, String objectName, String fileName) {
        String filePath = builderFilePath(prefix, objectName);
        try {
            UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filePath).filename(fileName).build();
            minioClient.uploadObject(uploadObjectArgs);
            return minioConfig.getEndpoint() + separator + minioConfig.getBucketName() +
                    separator +
                    filePath;
        } catch (Exception e) {
            log.error("minio upload file error.", e);
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 下载文件url到本地指定文件夹
     *
     * @param fileUrl      文件url
     * @param templatePath 指定路径文件夹
     * @return path 本地文件全路径
     */
    @Override
    public String downloadFile(String fileUrl, String templatePath) {
        String filePath = getFilePathByFileUrl(fileUrl);
        InputStream inputStream = null;
        String filename = filePath.substring(filePath.lastIndexOf(separator) + 1);
        try {
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filePath).build());

            FileOutputStream outputStream = null;
            outputStream = new FileOutputStream(templatePath + filename);
            byte[] buff = new byte[100];
            int rc = 0;
            while (true) {
                try {
                    if (!((rc = inputStream.read(buff, 0, 100)) > 0)) {
                        break;
                    }
                } catch (IOException ignored) {
                    log.error("文件read失败");

                }
                outputStream.write(buff, 0, rc);
            }
            outputStream.close();
            inputStream.close();
        } catch (Exception e) {
            log.error("minio down file error.  pathUrl:{}", fileUrl);
            throw new RuntimeException("下载文件url到本地指定文件夹失败");
        }
        return templatePath + filename;
    }

    /**
     * 下载文件
     *
     * @param fileUrl 文件全路径
     * @return 文件流
     */
    @Override
    public void downloadFile(String fileUrl, HttpServletResponse response) {
        String filePath = getFilePathByFileUrl(fileUrl);

        String fileName = "";
        if (filePath != null) {
            int lastSlashIndex = filePath.lastIndexOf('/');

            // 截取 URL 中最后一个斜杠后面的部分，即文件名
            fileName = filePath.substring(lastSlashIndex + 1);
        }
        try {
            response.setContentType("application/octet-stream");
            //设置响应头
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName,
                            "UTF-8"));
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filePath).build());

            BufferedInputStream buffInputStream = new BufferedInputStream(inputStream, 1024 * 10);
            //设置文件大小
            byte buf[] = new byte[1024 * 10];
            int length = 0;
            OutputStream outputStream = response.getOutputStream();
            while ((length = buffInputStream.read(buf, 0, 1024 * 10)) != -1) {
                outputStream.write(buf, 0, length);
            }
            response.setContentLength(length);
            response.flushBuffer();
            //关闭流
            buffInputStream.close();
            inputStream.close();
            outputStream.close();
            log.info("文件导出:" + fileUrl);
        } catch (Exception e) {
            log.error("minio down file error.  pathUrl:{}", fileUrl);
            throw new RuntimeException("下载文件失败");
        }
    }

    /**
     * 删除文件
     *
     * @param fileUrl 文件url
     */
    @Override
    public void deleteFile(String fileUrl) {
        String filePath = getFilePathByFileUrl(fileUrl);
        // 构建参数
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(filePath).build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}", fileUrl);
            throw new RuntimeException("删除文件失败");
        }
    }

    /**
     * 查询
     * @param path
     * @return
     */
    @Override
    public List<FileVO> listObjects(String path) {

        Boolean onlyDir = false;
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .prefix(path)
                        .recursive(true)
                        .build());
        List<FileVO> objectItems = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                if (onlyDir && !item.isDir()) {
                    continue;
                }
                if (!onlyDir && item.isDir()) {
                    continue;
                }
                FileVO objectItem = new FileVO();

                String[] paths = item.objectName().split("/");
                String url = minioConfig.getPreview() + separator + minioConfig.getBucketName() +
                        separator + item.objectName();

                objectItem.setModifiedTime(item.lastModified().toLocalDateTime());
                objectItem.setUrl(url);
                objectItem.setSrc(paths[paths.length - 1]);
                objectItem.setFileName(getFilePathByFileUrl(paths[paths.length - 1]));
                objectItems.add(objectItem);
            }
        } catch (Exception e) {
            return null;
        }
        return objectItems;

    }


}
